using Clancy.Data;
using Clancy.Interfaces;
using Clancy.Service.Client.ClancyServiceClient;
using Clancy.Service.Client.LiveStreamFeedbackTcpClient;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Subjects;
using System.ServiceModel;
using System.Threading.Tasks;

namespace Clancy.Service.Client
{
    public class ServerConnectionLogic : IServerConnectionLogic
    {
        private readonly BehaviorSubject<ConnectionStatus> m_ConnectionStatusOSubject = new BehaviorSubject<ConnectionStatus>(Interfaces.ConnectionStatus.NotConnected);
        private readonly ISurveilanceStorage m_SurveilanceStorage;
        private IClancyCommunicationServiceCallback m_CallbackService;
        private ClancyCommunicationServiceClient m_Client;
        private LiveStreamInterfaceClient m_LifeFeedbackClient;
        private LiveFeedbackCallbackservice m_LiveFeedbackCallbackService;
        private IOperationAdapter m_OperationAdapter;

        public ServerConnectionLogic(ISurveilanceStorage surveilanceStorage)
        {
            m_SurveilanceStorage = surveilanceStorage;
        }

        public IClancyCommunicationServiceCallback CallbackService
        {
            get { return m_CallbackService; }
        }

        public IObservable<ConnectionStatus> ConnectionStatus
        {
            get { return m_ConnectionStatusOSubject; }
        }

        public LiveStreamInterfaceClient FeedbackClient
        {
            get { return m_LifeFeedbackClient; }
        }

        public ClancyCommunicationServiceClient RemotingClient
        {
            get { return m_Client; }
        }

        public async void AddNewSource(IVideoSourceFilterInfo videoSourceFilterInfo)
        {
            await m_Client.AddNewSourceAsync(videoSourceFilterInfo as VideoSourceFilterInfo);
        }

        public virtual async Task<bool> Connect(IOperationAdapter operationAdapter)
        {
            m_ConnectionStatusOSubject.OnNext(Interfaces.ConnectionStatus.Conntecting);
            m_OperationAdapter = operationAdapter;
            try
            {
                // http://adamprescott.net/2012/08/15/a-simple-wcf-service-callback-example/

                m_CallbackService = new ClancyCommunicationCallbackService(m_OperationAdapter, m_SurveilanceStorage);
                m_LiveFeedbackCallbackService = new LiveFeedbackCallbackservice(m_OperationAdapter);

                InstanceContext instanceContextMainService = new InstanceContext(m_CallbackService);
                m_Client = new ClancyCommunicationServiceClient(instanceContextMainService);

                bool mainConnected = await RemotingClient.ConnectAsync();
                if (mainConnected)
                {
                    InstanceContext instanceContextLiveFeedbackService =
                        new InstanceContext(m_LiveFeedbackCallbackService);
                    m_LifeFeedbackClient = new LiveStreamInterfaceClient(instanceContextLiveFeedbackService);
                    await m_LifeFeedbackClient.SubsribeForRecordingsAsync();

                    m_ConnectionStatusOSubject.OnNext(Interfaces.ConnectionStatus.Connected);
                }
                else
                {
                    m_ConnectionStatusOSubject.OnNext(Interfaces.ConnectionStatus.ConnectionFailed);
                }

                return mainConnected;
            }
            catch (EndpointNotFoundException)
            {
                m_ConnectionStatusOSubject.OnNext(Interfaces.ConnectionStatus.ConnectionFailed);
            }
            catch (TimeoutException)
            {
                m_ConnectionStatusOSubject.OnNext(Interfaces.ConnectionStatus.ConnectionFailed);
            }
            return false;
        }

        public async Task<bool> DeleteRecording(Guid selectedRecordingId)
        {
            return await m_Client.DeleteRecordingAsync(selectedRecordingId);
        }

        public virtual async void Disconnect()
        {
            if (m_Client != null)
            {
                await m_LifeFeedbackClient.CloseAllAsync();
                await m_LifeFeedbackClient.UnsubsribeForRecordingsAsync();

                await m_Client.DisconnectAsync();
                m_Client = null;
            }
            m_ConnectionStatusOSubject.OnNext(Interfaces.ConnectionStatus.NotConnected);
        }

        public ISurveilanceObject[] GetAllSurveilances()
        {
            return m_Client.GetSureveilanceObjects().Cast<ISurveilanceObject>().ToArray();
        }

        public IEnumerable<IVideoSourceFilterInfo> GetAvailableSources()
        {
            return m_Client.GetAvailableSources();
        }

        public async void RemoveSource(ISurveilanceObject surveilanceObject)
        {
            await m_Client.DeleteVideoSourceAsync(surveilanceObject as SurveilanceObject);
        }

        public Task<bool> StartVideostream(ISurveilanceObject surveilanceObject)
        {
            if (surveilanceObject == null)
                return Task.FromResult(false);
            return m_LifeFeedbackClient.OpenAsync(surveilanceObject.Id);
        }

        public Task StopVideostream(ISurveilanceObject surveilanceObject)
        {
            if (surveilanceObject == null)
                return Task.FromResult(false);
            m_LifeFeedbackClient.CloseAsync(surveilanceObject.Id);
            return Task.FromResult(true);
        }

        public async Task<bool> UpdateSurveilanceObject(ISurveilanceObject surveilanceObject)
        {
            SurveilanceObject survObj = surveilanceObject as SurveilanceObject;
            if (survObj == null)
                return false;

            return await m_Client.UpdateSurveilanceAsync(survObj);
        }

        public async Task<bool> UpdateSurveilanceObject(Guid surveilanceObjectId, string propertyName, object value)
        {
            if (m_Client == null)
                return false;
            return await m_Client.UpdateSurveilancePropertyAsync(surveilanceObjectId, propertyName, value.ToString());
        }
    }
}